package cn.buk.tms.train.dao;

import cn.buk.tms.dao.AbstractDao;
import cn.buk.tms.entity.misc.OperationLog;
import cn.buk.tms.entity.order.train.TrainRefundOrder;
import cn.buk.tms.entity.order.train.TrainRefundOrder_;
import cn.buk.tms.factory.OperationLogFactory;
import cn.buk.tms.train.sc.TrainOrderSearchCriteria;
import cn.buk.common.util.DateUtil;
import jakarta.persistence.criteria.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

import jakarta.transaction.Transactional;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import static cn.buk.tms.common.constant.TmsOrderConstant.OPERATING_OBJECT_TRAIN_REFUND_ORDER;

@Transactional
@Component
public class TrainRefundOrderDaoImpl extends AbstractDao implements TrainRefundOrderDao {

    private static final Logger logger = LogManager.getLogger(TrainRefundOrderDaoImpl.class);

    /**
     * 保存火车票退票单
     * @param o
     * @return
     */
    @Override
    public int createOrder(TrainRefundOrder o) {
        em.persist(o);

        OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
        log.setEnterpriseId(o.getEnterpriseId());
        log.setOrderId(o.getId());
        log.setOperator(o.getOperator());
        log.setContent("创建退票单");

        em.persist(log);

        return o.getId();
    }

  @Override
  public int createOrders(List<TrainRefundOrder> list) {
      for(TrainRefundOrder o: list) {
        em.persist(o);

        OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
        log.setEnterpriseId(o.getEnterpriseId());
        log.setOrderId(o.getId());
        log.setOperator(o.getOperator());
        log.setContent("批量创建退票单");

        em.persist(log);
      }

    return list.size();
  }

  @Override
    public TrainRefundOrder getOrderById(int id) {
        return em.find(TrainRefundOrder.class, id);
    }

    private List<Predicate> buildPredicates_searchTrainOrders(CriteriaBuilder cb, Root<TrainRefundOrder> root,
                                                            int enterpriseId, TrainOrderSearchCriteria sc, List<Integer> allowedCustomerIds) {

        List<Predicate> predicates = new ArrayList<>();

        Predicate predicate = cb.conjunction();
        predicates.add(predicate);

        predicate = cb.equal(root.get(TrainRefundOrder_.enterpriseId), enterpriseId);
        predicates.add(predicate);

        if (sc.getCustomerId() == 0) {
            // 散客
            predicate = cb.equal(root.get(TrainRefundOrder_.customerType), 0);
            predicates.add(predicate);
        } else if (sc.getCustomerId() > 0) {
            // 指定客户
            predicate = cb.equal(root.get(TrainRefundOrder_.customer), sc.getCustomerId());
            predicates.add(predicate);
        }

        if (sc.getBeginDate() != null && sc.getEndDate() != null) {
            predicate = cb.greaterThanOrEqualTo(root.get(TrainRefundOrder_.createTime), sc.getBeginDate());
            predicate = cb.and(predicate,
                    cb.lessThanOrEqualTo(root.get(TrainRefundOrder_.createTime), sc.getEndDate()));
            predicates.add(predicate);
        } else if (sc.getBeginDate() != null) {
            predicate = cb.greaterThanOrEqualTo(root.get(TrainRefundOrder_.createTime), sc.getBeginDate());
            predicates.add(predicate);
        } else if (sc.getEndDate() != null) {
            predicate = cb.lessThanOrEqualTo(root.get(TrainRefundOrder_.createTime), sc.getEndDate());
            predicates.add(predicate);
        }

        if (sc.getName() != null && sc.getName().length() > 0) {
            // 入住人姓名
            predicate = cb.like(root.get(TrainRefundOrder_.passengerName), "%" + sc.getName() + "%");
            predicates.add(predicate);
        }

        if (sc.getDepartureDate() != null && sc.getDepartureDate().length() == 10) {
            // 出发日期
            try {
                predicate = cb.equal(root.get(TrainRefundOrder_.ddate),
                        DateUtil.convertToDate(sc.getDepartureDate(), "yyyy-MM-dd"));
                predicates.add(predicate);
            } catch (ParseException e) {
                logger.error(e.getMessage());
            }
        }

        if (sc.getOrderNo().length() > 0) {
            predicate = cb.equal(root.get(TrainRefundOrder_.orderNo), sc.getOrderNo());
            predicates.add(predicate);
        }

        if (sc.getStatus() != -1) {
            predicate = cb.equal(root.get(TrainRefundOrder_.status), sc.getStatus());
        } else {
            predicate = cb.notEqual(root.get(TrainRefundOrder_.status), TrainRefundOrder.STATUS_CANCELED);
        }
        predicates.add(predicate);

        if (sc.getOp1() != null && sc.getOp1().trim().length() > 0) {
            predicate = cb.equal(root.get(TrainRefundOrder_.operator), sc.getOp1());
            predicates.add(predicate);
        }

        if (allowedCustomerIds != null && allowedCustomerIds.size() > 0) {
            // 只允许查看指定的企业客户的订单
            predicate = cb.in(root.get(TrainRefundOrder_.customer).get("id")).value(allowedCustomerIds);
            predicates.add(predicate);
        }

        return predicates;
    }

    /**
     * search train refund orders
     * @param enterpriseId
     * @param sc
     * @param allowedCustomerIds
     * @return
     */
    @Override
    public List<TrainRefundOrder> searchOrders(final int enterpriseId, TrainOrderSearchCriteria sc, List<Integer> allowedCustomerIds) {
      List<TrainRefundOrder> results = null;

      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery<TrainRefundOrder> cq = cb.createQuery(TrainRefundOrder.class);
      Root<TrainRefundOrder> root = cq.from(TrainRefundOrder.class);
      root.alias("o");

      var predicates0 =  buildPredicates_searchTrainOrders(cb, root, enterpriseId, sc, allowedCustomerIds);

      //计算根据条件查询得出的数据总数
      CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
      Root<TrainRefundOrder> countRoot = countQuery.from(TrainRefundOrder.class);
      countRoot.alias("o");

        var predicates1 =  buildPredicates_searchTrainOrders(cb, countRoot, enterpriseId, sc, allowedCustomerIds);

      // count items
      countQuery.select(cb.count(countRoot))
          .where(predicates1.toArray(new Predicate[predicates1.size()]));
      Long count = em.createQuery(countQuery).getSingleResult();

      //查询
      cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

      List<Order> orderByes = new ArrayList<>();
      orderByes.add(cb.desc(root.get("id")));

      cq.orderBy(orderByes);

      int maxResults = count.intValue();
      if (maxResults > 0) {
        sc.getPage().setRowCount(maxResults);

        results = em.createQuery(cq)
            .setFirstResult(sc.getPage().getFirstPosition())
            .setMaxResults(sc.getPage().getPageSize())
            .getResultList();
      }

      if (results == null) {
        results = new ArrayList<>();
      }

      return results;
    }

    /**
     * 修改酒店订单备注
     * @param enterpriseId
     * @param username
     * @param orderId
     * @param remark
     * @return
     */
    @Override
    public int updateOrderRemark(final int enterpriseId, final String username, final int orderId, final String remark) {
        TrainRefundOrder o = em.find(TrainRefundOrder.class, orderId);
        if (o.getEnterpriseId() != enterpriseId) {
            throw new RuntimeException("TrainRefundOrder is not existed.");
        }

        o.setRemark(remark);
        em.merge(o);

        OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
        log.setEnterpriseId(o.getEnterpriseId());
        log.setOrderId(o.getId());
        log.setOperator(username);

        String content = "修改备注为: " + remark;
        log.setContent(content);
        em.persist(log);

        return 1;
    }

    @Override
    public int updateOrderExternalOrderNo(int enterpriseId, String username, int orderId, String externalOrderNo) {
        TrainRefundOrder o = em.find(TrainRefundOrder.class, orderId);
        if (o.getEnterpriseId() != enterpriseId) {
            throw new RuntimeException("TrainRefundOrder is not existed.");
        }

        o.setExternalOrderNo(externalOrderNo);
        em.merge(o);

        OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
        log.setEnterpriseId(o.getEnterpriseId());
        log.setOrderId(o.getId());
        log.setOperator(username);

        String content = "修改外部订单号为: " + externalOrderNo;
        log.setContent(content);
        em.persist(log);

        return 1;
    }



    @Override
    public int updateOrderPrice(int enterpriseId, String username, int orderId, double price, double cost, double serviceCharge, double totalAmount, double totalCost, double profit) {
        int retCode = em.createQuery("update TrainRefundOrder o set o.price = :price, o.cost = :cost, o.serviceCharge = :serviceCharge, " +
                "o.totalAmount = :totalAmount, o.totalCost = :totalCost, o.profit = :profit, o.lastUpdate = :lastUpdate " +
                "where o.id = :id and o.enterpriseId = :enterpriseId")
                .setParameter("id", orderId)
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("price", price)
                .setParameter("cost", cost)
                .setParameter("serviceCharge", serviceCharge)
                .setParameter("totalAmount", totalAmount)
                .setParameter("totalCost", totalCost)
                .setParameter("profit", profit)
                .setParameter("lastUpdate", DateUtil.getCurDateTime())
                .executeUpdate();
        if (retCode == 1) {
            OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
            log.setEnterpriseId(enterpriseId);
            log.setOrderId(orderId);
            log.setOperator(username);
            log.setContent("修改价格为：" + price + ", " + cost + ", " + serviceCharge);
            em.persist(log);
        }

        return retCode;
    }


    @Override
    public int processOrder(int enterpriseId, String username, int orderId) {
        int retCode = em.createQuery("update TrainRefundOrder o set o.status = :newStatus, o.ticketer = :ticketer, o.lastUpdate = :lastUpdate " +
                "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
                .setParameter("id", orderId)
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("oldStatus", TrainRefundOrder.STATUS_WAITING)
                .setParameter("newStatus", TrainRefundOrder.STATUS_PROCESSING)
                .setParameter("ticketer", username)
                .setParameter("lastUpdate", DateUtil.getCurDateTime())
                .executeUpdate();
        if (retCode != 1) {
          throw new RuntimeException("update status failed.");
        }

        OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
        log.setEnterpriseId(enterpriseId);
        log.setOrderId(orderId);
        log.setOperator(username);

        String content = "处理订单";
        log.setContent(content);
        em.persist(log);

        return 1;
    }

    @Override
    public int finishOrder(int enterpriseId, String username, int orderId, String remark) {
        int retCode = em.createQuery("update TrainRefundOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
                "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
                .setParameter("id", orderId)
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("oldStatus", TrainRefundOrder.STATUS_PROCESSING)
                .setParameter("newStatus", TrainRefundOrder.STATUS_DONE)
                .setParameter("lastUpdate", DateUtil.getCurDateTime())
                .executeUpdate();
        if (retCode != 1) {
          throw new RuntimeException("update status failed.");
        }

        OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
        log.setEnterpriseId(enterpriseId);
        log.setOrderId(orderId);
        log.setOperator(username);

        String content = "订单处理完毕(" + remark + ")";
        log.setContent(content);
        em.persist(log);

        return 1;
    }

    @Override
    public List<OperationLog> searchOrderHistory(int enterpriseId, int orderId) {
        return searchOrderHistory(enterpriseId, OPERATING_OBJECT_TRAIN_REFUND_ORDER, orderId);
    }

    /**
     * 取消酒店订单
     * @param enterpriseId
     * @param username
     * @param hotelOrderId
     * @param remark
     * @return
     */
    @Override
    public int cancelOrder(int enterpriseId, String username, int hotelOrderId, String remark) {
        int retCode = em.createQuery("update TrainRefundOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
                "where o.id = :id and o.enterpriseId = :enterpriseId and o.status in (:oldStatus1, :oldStatus2)")
                .setParameter("id", hotelOrderId)
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("oldStatus1", TrainRefundOrder.STATUS_WAITING)
                .setParameter("oldStatus2", TrainRefundOrder.STATUS_PROCESSING)
                .setParameter("newStatus", TrainRefundOrder.STATUS_CANCELED)
                .setParameter("lastUpdate", DateUtil.getCurDateTime())
                .executeUpdate();
        if (retCode != 1) {
          throw new RuntimeException("To cancel hotel order failed.");
        }

        OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
        log.setEnterpriseId(enterpriseId);
        log.setOperator(username);
        log.setOrderId(hotelOrderId);
        log.setContent("取消订单： " + remark);
        em.persist(log);

        return retCode;
    }

    /**
     * 回滚酒店订单
     * @param enterpriseId
     * @param username
     * @param orderId
     * @return
     */
    @Override
    public int rollbackOrder(int enterpriseId, String username, int orderId, final String remark) {
        int retCode = em.createQuery("update TrainRefundOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
                "where o.id = :id and o.enterpriseId = :enterpriseId and o.status in (:oldStatus1)")
                .setParameter("id", orderId)
                .setParameter("enterpriseId", enterpriseId)
                .setParameter("oldStatus1", TrainRefundOrder.STATUS_DONE)
                .setParameter("newStatus", TrainRefundOrder.STATUS_PROCESSING)
                .setParameter("lastUpdate", DateUtil.getCurDateTime())
                .executeUpdate();
        if (retCode != 1) {
          throw new RuntimeException("To rollback train refund order failed.");
        }

        OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
        log.setEnterpriseId(enterpriseId);
        log.setOperator(username);
        log.setOrderId(orderId);
        log.setContent("退票单回滚: " + remark);
        em.persist(log);

        return retCode;
    }

  @Override
  public List<TrainRefundOrder> searchUnpaidOrders(int max) {
    return em.createQuery("select o from TrainRefundOrder o " +
            "where (o.payStatus is null or o.payStatus <> 2) " +
            "and o.status = :status " +
            "and o.createTime >= :createTime " +
            "order by o.id", TrainRefundOrder.class)
            .setParameter("status", TrainRefundOrder.STATUS_DONE)
            .setParameter("createTime", getMinBeginDate())
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public int setOrderWriteOff(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update TrainRefundOrder o set " +
            "o.payStatus = 2 " +
            "where o.id = :id and o.enterpriseId = :enterpriseId and (o.payStatus is null or o.payStatus <> 2)")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createTrainRefundOrderLog(enterpriseId, orderId, username, "已销账：" + remark);
      em.persist(log);
    }

    return retCode;
  }

  private OperationLog createTrainRefundOrderLog(int enterpriseId, int orderId, String username, String s) {
    OperationLog log = OperationLogFactory.createTrainRefundOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOrderId(orderId);
    log.setOperator(username);
    log.setContent(s);

    return log;
  }
}
